package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.global;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelExceptionConfigTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.MagicApiConfigTaskEventEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.magicApiConfig.BpmCallMagicApiService;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.magicApiConfig.dto.MagicApiConfigDto;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.exception.ExceptionConfig;
import cn.iocoder.yudao.module.bpm.framework.flowable.exception.ExceptionConfigCheck;
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import com.google.common.collect.ImmutableSet;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BaseElement;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;

/**
 * @description: 任务Event全局监听
 */
@Component
@Slf4j
public class GlobalTaskEventListener extends AbstractFlowableEngineEventListener {

    @Resource
    @Lazy // 解决循环依赖
    private BpmTaskService bpmTaskService;
    @Resource
    @Lazy // 解决循环依赖
    private BpmActivityService activityService;
    @Resource
    @Lazy
    private BpmProcessInstanceService processInstanceService;
    @Resource
    @Lazy
    private BpmModelService bpmModelService;
    @Resource
    @Lazy
    private BpmCallMagicApiService bpmCallMagicApiService;
    @Resource
    @Lazy
    private ExceptionConfigCheck exceptionConfigCheck;
    @Resource
    @Lazy // 解决循环依赖
    private TaskService taskService;


    public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
            .add(FlowableEngineEventType.TASK_CREATED)
            .add(FlowableEngineEventType.TASK_ASSIGNED)
            .add(FlowableEngineEventType.TASK_COMPLETED)
            .add(FlowableEngineEventType.ACTIVITY_CANCELLED)
            .build();

    public GlobalTaskEventListener(){
        super(TASK_EVENTS);
    }

    @Override
    protected void taskCreated(FlowableEngineEntityEvent event) {

        //得到流程定义id
        String processDefinitionId = event.getProcessDefinitionId();
        //得到流程实例id
        String processInstanceId = event.getProcessInstanceId();
        //得到任务实例
        TaskEntity task = (TaskEntity) event.getEntity();

        BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
        ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
        BaseElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());

        List<MagicApiConfigDto> magicApiConfigDtoList = bpmCallMagicApiService.initMagicApiConfig(source);
        magicApiConfigDtoList.stream().forEach(magicApiConfigDto -> {
            if(MagicApiConfigTaskEventEnum.CREATED.getEvent().equals(magicApiConfigDto.getEvent())){
                callBpmCallMagicApiService( magicApiConfigDto,processInstance,task);
            }
        });

        // 查看是否自动完成
        if (StringUtils.isBlank(task.getAssignee())){
            ExceptionConfig exceptionConfig = exceptionConfigCheck.getTaskExceptionConfig(processDefinitionId);
            if(exceptionConfig.getExceptionEvent() == BpmModelExceptionConfigTypeEnum.STEP_TASK){
                // 情况三：审批普通的任务。大多数情况下，都是这样
                // 3.1 更新 task 状态、原因
                bpmTaskService.updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), "【异常规则】"+BpmModelExceptionConfigTypeEnum.STEP_TASK.getName());
                // 3.2 添加评论
                taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
                        BpmCommentTypeEnum.APPROVE.formatComment("【异常规则】"+BpmModelExceptionConfigTypeEnum.STEP_TASK.getName()));
                // 自动完成
                taskService.complete((task).getId());
            }
        }

        log.info("任务创建，流程定义id：{}，流程实例id：{}，任务名称：{}，任务Key：{}，任务优先级：{}，任务处理人：{}",
                processDefinitionId,
                processInstanceId,
                task.getName(),
                task.getTaskDefinitionKey(),
                task.getPriority(),
                task.getAssignee()
        );
    }

    @Override
    protected void taskCompleted(FlowableEngineEntityEvent event) {

        //得到流程定义id
        String processDefinitionId = event.getProcessDefinitionId();
        //得到流程实例id
        String processInstanceId = event.getProcessInstanceId();
        //得到任务实例
        TaskEntity task = (TaskEntity) event.getEntity();

        BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
        ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
        BaseElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());

        List<MagicApiConfigDto> magicApiConfigDtoList = bpmCallMagicApiService.initMagicApiConfig(source);
        magicApiConfigDtoList.stream().forEach(magicApiConfigDto -> {
            if(MagicApiConfigTaskEventEnum.COMPLETED.getEvent().equals(magicApiConfigDto.getEvent())){
                callBpmCallMagicApiService( magicApiConfigDto,processInstance,task);
            }
        });
        log.info("任务完成，流程定义id：{}，流程实例id：{}，任务名称：{}，任务Key：{}，任务优先级：{}，任务处理人：{}",
                processDefinitionId,
                processInstanceId,
                task.getName(),
                task.getTaskDefinitionKey(),
                task.getPriority(),
                task.getAssignee()
        );
    }


    @Override
    protected void taskAssigned(FlowableEngineEntityEvent event) {

        //得到流程定义id
        String processDefinitionId = event.getProcessDefinitionId();
        //得到流程实例id
        String processInstanceId = event.getProcessInstanceId();
        //得到任务实例
        TaskEntity task = (TaskEntity) event.getEntity();

        BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
        ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
        BaseElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());

        List<MagicApiConfigDto> magicApiConfigDtoList = bpmCallMagicApiService.initMagicApiConfig(source);
        magicApiConfigDtoList.stream().forEach(magicApiConfigDto -> {
            if(MagicApiConfigTaskEventEnum.ASSIGNMENT.getEvent().equals(magicApiConfigDto.getEvent())){
                callBpmCallMagicApiService( magicApiConfigDto,processInstance,task);
            }
        });
        log.info("任务被指派，流程定义id：{}，流程实例id：{}，任务名称：{}，任务Key：{}，任务优先级：{}，任务处理人：{}",
                processDefinitionId,
                processInstanceId,
                task.getName(),
                task.getTaskDefinitionKey(),
                task.getPriority(),
                task.getAssignee()
        );
    }

    @Override
    protected void activityCancelled(FlowableActivityCancelledEvent event) {

        //得到流程定义id
        String processDefinitionId = event.getProcessDefinitionId();
        //得到流程实例id
        String processInstanceId = event.getProcessInstanceId();

        List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId());
        if (CollUtil.isEmpty(activityList)) {
            log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());
            return;
        }
        // 遍历处理
        activityList.forEach(activity -> {
            if (StrUtil.isEmpty(activity.getTaskId())) {
                return;
            }
            Task task = bpmTaskService.getTask(activity.getTaskId());
            if(task == null){
                return;
            }

            BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
            ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
            BaseElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());

            List<MagicApiConfigDto> magicApiConfigDtoList = bpmCallMagicApiService.initMagicApiConfig(source);
            magicApiConfigDtoList.stream().forEach(magicApiConfigDto -> {
                if(MagicApiConfigTaskEventEnum.CANCELLED.getEvent().equals(magicApiConfigDto.getEvent())){
                    callBpmCallMagicApiService( magicApiConfigDto,processInstance,task);
                }
            });
            log.info("任务被取消，流程定义id：{}，流程实例id：{}，任务名称：{}，任务Key：{}，任务优先级：{}，任务处理人：{}",
                    processDefinitionId,
                    processInstanceId,
                    task.getName(),
                    task.getTaskDefinitionKey(),
                    task.getPriority(),
                    task.getAssignee()
            );
        });
    }


    private void callBpmCallMagicApiService(MagicApiConfigDto magicApiConfigDto,ProcessInstance processInstance,Task task){
        magicApiConfigDto.getFields()
                .put("processInstanceId", processInstance.getProcessInstanceId());
        magicApiConfigDto.getFields()
                .put("processDefinitionKey", processInstance.getProcessDefinitionKey());
        magicApiConfigDto.getFields()
                .put("businessKey", processInstance.getBusinessKey());
        magicApiConfigDto.getFields()
                .put("event", magicApiConfigDto.getEvent());
        magicApiConfigDto.getFields()
                .put("taskDefinitionKey", task.getTaskDefinitionKey());
        magicApiConfigDto.getFields()
                .put("taskName", task.getName());

        magicApiConfigDto.getFields().putAll(processInstance.getProcessVariables());

        bpmCallMagicApiService.callMagicApiByConfig(magicApiConfigDto);
    }


}
